#pragma once
#include <sys/epoll.h>
#include "Channel.hpp"
#include <unordered_map>
#include <iostream>
#include <cstdlib>
#include <cerrno>
#include <cstring>
#include <cassert>
#include <vector>

#define MAXEPOLLEVENTS 1024
class Poller
{
private:
    int _epfd;
    struct epoll_event _evts[MAXEPOLLEVENTS];
    std::unordered_map<int, Channel *> _channels;

    void Update(Channel *channel, int op)
    {
        int fd = channel->Fd();
        struct epoll_event ev;
        ev.data.fd = fd;
        ev.events = channel->Events();
        int ret = epoll_ctl(_epfd, op, fd, &ev);
        if (ret < 0)
            std::cerr << "EPOLL CTRL FAILED! " << strerror(errno) << std::endl;
    }

    bool IsChannelAdd(Channel *channel)
    {
        auto it = _channels.find(channel->Fd());
        if (it == _channels.end())
            return false;
        return true;
    }

public:
    Poller()
    {
        _epfd = ::epoll_create(MAXEPOLLEVENTS);
        if (_epfd < 0)
        {
            std::cerr << "EPOLL CREATE FAILED!" << std::endl;
            abort();
        }
    }
    ~Poller() {}

    // 添加/修改事件监控
    void UpdateEvent(Channel *channel)
    {
        if (!IsChannelAdd(channel))
        {
            _channels.insert(std::make_pair(channel->Fd(), channel));
            Update(channel, EPOLL_CTL_ADD);
        }
        else
        {
            Update(channel, EPOLL_CTL_MOD);
        }
    }

    // 移除事件监控
    void RemoveEvent(Channel *channel)
    {
        auto it = _channels.find(channel->Fd());
        if (it != _channels.end())
            _channels.erase(it);
        Update(channel, EPOLL_CTL_DEL);
    }

    // 开始监控，返回活跃链接
    void Poll(std::vector<Channel *> *activate)
    {
        int nfds = ::epoll_wait(_epfd, _evts, MAXEPOLLEVENTS, -1);
        if (nfds < 0)
        {
            if (errno == EINTR)
                return;
            std::cerr << "EPOLL WAIT ERROR! " << strerror(errno) << std::endl;
            abort();
        }

        for (int i = 0; i < nfds; ++i)
        {
            auto it = _channels.find(_evts[i].data.fd);
            assert(it != _channels.end());
            it->second->SetREvevt(_evts[i].events);
            activate->push_back(it->second);
        }
    }
};
